/*
board-games:    An educational framework for board games programming.
    Copyright (C) 2008  Daniel Gutson, FuDePAN
    
    This file is part of board-games.

    board-games is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    board-games is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
*/

#ifndef BOARD_H
#define BOARD_H

//#include <sys/shm.h>

class SharedBoard
{
public:
    int share_create(size_t boardId);
    void share_connect(int shmid);
protected:
    void share_destroy();

protected:
    virtual size_t size() const = 0;
    virtual void*  get_mem() = 0;
    virtual void copy_to(void* address) = 0;
    virtual void remap(void* new_address) = 0;
    
    enum ConnectionState
    {
        Disconnected,
        ConnectedMaster,
        ConnectedSlave
    };
    
    SharedBoard() : state(Disconnected) {}
    
private:
    int shmid;
    //void* shmaddr;
    ConnectionState state;
};

template <class CellType, size_t Rows, size_t Cols>
class Board : public SharedBoard
{
public:
    typedef CellType BoardType[Rows][Cols];

private:
    BoardType*  _cells_ptr;
    bool        remapped;

//    static const size_t PAGE_SIZE = SHMLBA; //4096;
#if 0
    static char* allocate()
    {
        const size_t pages = (sizeof(BoardType) / SHMLBA/*PAGE_SIZE*/) + 2; // round up + align gap
        return new char[pages * SHMLBA/*PAGE_SIZE*/];
    }
    
    static char* page_align(char* ptr)
    {
        const size_t delta = size_t(ptr) % SHMLBA/*PAGE_SIZE*/;
        if (delta == 0)
            return ptr;
        else
            return (SHMLBA/*PAGE_SIZE*/ - delta) + ptr;
    }
#endif

public:
    Board(const CellType& initValue)
//        : _buffer(allocate()), _cells(*reinterpret_cast<BoardType*>(page_align(_buffer)))
        : _cells_ptr(new BoardType[1]), remapped(false)
    {
        for(size_t row=0; row<Rows; ++row)
            for(size_t col=0; col<Cols; ++col)
                get_cell(row, col) = initValue;
    }
    
    Board()
        : _cells_ptr(new BoardType[1]), remapped(false)
        //: _buffer(allocate()), _cells(*reinterpret_cast<BoardType*>(page_align(_buffer)))
    {}
    
    ~Board()
    {
        share_destroy();
        if (!remapped)
        {
            delete [] _cells_ptr;
        }
    }

    const CellType&  get_cell(size_t row, size_t col) const { return (*_cells_ptr)[row][col]; }
    CellType&        get_cell(size_t row, size_t col)       { return (*_cells_ptr)[row][col]; }
    
    size_t number_of_cells() const { return Rows * Cols; }

    struct _Row
    {
        Board& brd;
        const size_t row;

        CellType& operator[](size_t col) const { return brd.get_cell(row, col); }
        _Row(Board& brd, size_t row) : brd(brd), row(row) {}
    };

    struct _CRow
    {
        const Board& brd;
        const size_t row;

        const CellType& operator[](size_t col) const { return brd.get_cell(row, col); }
        _CRow(const Board& brd, size_t row) : brd(brd), row(row) {}
    };

    _Row  operator[](size_t row)       { return  _Row(*this, row); }
    _CRow operator[](size_t row) const { return _CRow(*this, row); }

    const BoardType& get_board_memory() const { return *_cells_ptr; }

private:
    virtual size_t size() const         { return sizeof(BoardType); }
    virtual void*  get_mem()            { return *_cells_ptr;         }

    virtual void copy_to(void* address)
    {
        // copy data:
        for(size_t row=0; row < Rows; ++row)
        {
            for(size_t col=0; col < Cols; ++col)
            {
                (*reinterpret_cast<BoardType*>(address))[row][col] = get_cell(row, col);
            }
        }
    }

    virtual void remap(void* new_address)
    {
        remapped = true;
        delete _cells_ptr;
        _cells_ptr = reinterpret_cast<BoardType*>(new_address);
    }

};

#endif


